﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WMM.Tapioca.Taxonomy;
using WMM.Tapioca.Linkbase;
using WMM.Tapioca.XbrlDimension;
using System.Xml;
using WMM.Tapioca.Instance;
using System.Xml.Linq;
using WMM.Tapioca.Utilities;

namespace WMM.Tapioca.Validate
{
    /// <summary>
    /// Class that validates the dimensional taxonomy.
    /// </summary>
    public class ValidateDimensionalTaxonomy
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        private DTS dts;

        /// <summary>
        /// The constructor.
        /// </summary>
        public ValidateDimensionalTaxonomy(DTS dts)
        {
            this.dts = dts;

            log.Info("Validating Dimensional Taxonomy, count(DRS):" + dts.DtsDimension.DimensionalRelationshipSets.Count);
            Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Validating Dimensional Taxonomy, count(DRS):" + dts.DtsDimension.DimensionalRelationshipSets.Count));

            ValidateTargetRoleInArcs();
            ValidateHypercubeIsAbstract();
            ValidateDimensionIsAbstract();
            ValidateConstraintsOnDimensionArcs();
            ValidateCycleInPrimaryItem();
            VerifyOccurrencesOnDimensionDefaultArcs();
            ValidateConstraintsOnTypedDomainRefAttribute();
            ValidateCycleInDRS();
        }        


        /// <summary>
        /// XDT 1.0 Specification 2.4.3
        /// If the value, V, of the @xbrldt:targetRole attribute on a definitionArc is not the standard extended 
        /// link role (http://www.xbrl.org/2003/role/link), the ancestor linkbase element of the definitionArc element
        /// MUST have a child roleRef element (3.5.2.4 [XBRL 2.1]) with V as the value of its @roleURI attribute (5.1.3 [XBRL 2.1]).
        /// </summary>
        private void ValidateTargetRoleInArcs()
        {
            Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Validating Dimensional Taxonomy: TargetRoleInArcs"));

            // A target role in arc element which is not referenced in roleref.
            foreach (NetworkGraph ng in dts.NetworkGraphs)
            {
                if (ng.TupleNG.Item1.Equals("definitionLink"))
                {
                    if (isCustomArcRole(ng.TupleNG.Item4))
                    {
                        foreach (Arc arc in ng.NetworkRelationships)
                        {
                            if (arc.TargetRole != null)
                            {
                                if (!isDefaultRole(arc.TargetRole.AbsoluteUri))
                                {
                                    foreach (RoleRef roleRef in dts.RoleRefs)
                                    {
                                        if (roleRef.ParentDocument == arc.ExtendedLinkParent.ParentDocument && roleRef.roleUri.Equals(arc.TargetRole.AbsoluteUri))
                                        {
                                            goto nextPoint;
                                        }
                                    }

                                    log.Error("Validation error on dimensional taxonomy: There is no a roleRef declaration to the targetRole attribute . TargetRole: " + arc.TargetRole);
                                    dts.ValidatedDimension = false;
                                }
                            }
                        }
                    }
                }
            }

        nextPoint: ;

            foreach (NetworkGraph ng in dts.NetworkGraphs)
            {
                if (ng.TupleNG.Item1.Equals("definitionLink"))
                {
                    if (isCustomArcRole(ng.TupleNG.Item4))
                    {                        
                        foreach (Arc arc in ng.NetworkRelationships)
                        {
                            if (arc.TargetRole != null)
                            {
                                foreach (NetworkGraph ng2 in dts.NetworkGraphs)
                                {
                                    if (ng2.TupleNG.Item2.Equals(arc.TargetRole.AbsoluteUri))
                                    {
                                        return;
                                    }
                                }

                                //log.Error("Validation error on dimensional taxonomy: The @xbrldt:targetRole attribute itself MUST contain a declared role on an extended link. targetRole: " + arc.TargetRole);
                                //dts.ValidatedDimension = false;
                            }
                        }
                    }
                }
            }
        }


        private void ValidateConstraintsOnDimensionArcs()
        {
            Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Validating Dimensional Taxonomy: ConstraintsOnDimensionArcs"));

            foreach (NetworkGraph ng in dts.NetworkGraphs)
            {
                if (ng.TupleNG.Item1.Equals("definitionLink"))
                {
                    if (isCustomArcRole(ng.TupleNG.Item4))
                    {
                        foreach (Arc arc in ng.NetworkRelationships)
                        {
                            // Source
                            Locator from = GetLocator(arc.ExtendedLinkParent.Locators, arc.FromId);
                            Concept conceptFrom = dts.GetConceptByID(from.HrefResourceId);

                            // Target
                            Locator to = GetLocator(arc.ExtendedLinkParent.Locators, arc.ToId);
                            Concept conceptTo = dts.GetConceptByID(to.HrefResourceId);

                            if (arc.ArcRole.AbsoluteUri.Equals("http://xbrl.org/int/dim/arcrole/all"))
                            {
                                // Constraints on "all" arcs

                                ValidateConstraintsOnAllArcs(arc, from, conceptFrom, to, conceptTo);
                            }
                        
                            if (arc.ArcRole.AbsoluteUri.Equals("http://xbrl.org/int/dim/arcrole/notAll"))
                            {
                                ValidateConstraintsOnNotAllArcs(arc, from, conceptFrom, to, conceptTo);
                            }
                        
                            if (arc.ArcRole.AbsoluteUri.Equals("http://xbrl.org/int/dim/arcrole/hypercube-dimension"))
                            {
                                ValidateConstraintsOnHypercubeDimensionArcs(arc, from, conceptFrom, to, conceptTo);
                            }
                       
                            if (arc.ArcRole.AbsoluteUri.Equals("http://xbrl.org/int/dim/arcrole/dimension-domain"))
                            {
                                ValidateConstraintsOnDimensionDomainArcs(arc, from, conceptFrom, to, conceptTo);
                            }
                        
                            if (arc.ArcRole.AbsoluteUri.Equals("http://xbrl.org/int/dim/arcrole/domain-member"))
                            {
                                ValidateConstraintsOnDomainMemberArcs(arc, from, conceptFrom, to, conceptTo);
                            }
                        
                            if (arc.ArcRole.AbsoluteUri.Equals("http://xbrl.org/int/dim/arcrole/dimension-default"))
                            {
                                ValidateConstraintsOnDimensionDefaultArcs(arc, from, conceptFrom, to, conceptTo);
                            }
                        }
                    }
                }
            }
        }


        private void ValidateConstraintsOnAllArcs(Arc arc, Locator from, Concept conceptFrom, Locator to, Concept conceptTo)
        {
            //Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Validating Dimensional Taxonomy: ConstraintsOnAllArcs"));
            
            // XDT 1.0 Specification 2.3.1.1
            //  A dimensional processor MUST raise an error xbrldte:HasHypercubeSourceError if the source of an all or notAll arc is not a primary item declaration [Def, 1].                

            if (!(conceptFrom.SubstitutionGroup == Concept.EnumSubstitutionGroup.Unknown) &&
                !(conceptFrom.SubstitutionGroup == Concept.EnumSubstitutionGroup.Item))
            {
                log.Error("Validation error on dimensional taxonomy:  if the source of an all arc is not a primary item declaration. Id of source: " + from.HrefResourceId);
                dts.ValidatedDimension = false;
            }
            else
            {
                if (conceptFrom.SubstitutionGroup == Concept.EnumSubstitutionGroup.Unknown)
                {
                    Concept otherConcept = dts.GetConceptByName(conceptFrom.SchemaElement.SubstitutionGroup.Namespace, conceptFrom.SchemaElement.SubstitutionGroup.Name);

                    if (!(otherConcept.SubstitutionGroup == Concept.EnumSubstitutionGroup.Item))
                    {
                        log.Error("Validation error on dimensional taxonomy:  if the source of an all arc is not a primary item declaration. Id of source: " + from.HrefResourceId);
                        dts.ValidatedDimension = false;
                    }
                }
            }

            // Specification 2.3.1.1
            //  A dimensional processor MUST raise an error xbrldte:HasHypercubeTargetError if the target of an "all" or "notAll" arc is not a hypercube declaration [Def, 4].                

            if (!(conceptTo.SubstitutionGroup == Concept.EnumSubstitutionGroup.Unknown) &&
                !(conceptTo.SubstitutionGroup == Concept.EnumSubstitutionGroup.HypercubeItem))
            {
                log.Error("Validation error on dimensional taxonomy: the target of an 'all' arc is not a hypercube declaration. Id of target: " + to.HrefResourceId);
                dts.ValidatedDimension = false;
            }
            else
            {
                if (conceptTo.SubstitutionGroup == Concept.EnumSubstitutionGroup.Unknown)
                {
                    Concept otherConcept = dts.GetConceptByName(conceptTo.SchemaElement.SubstitutionGroup.Namespace, conceptTo.SchemaElement.SubstitutionGroup.Name);

                    if (!(otherConcept.SubstitutionGroup == Concept.EnumSubstitutionGroup.HypercubeItem))
                    {
                        log.Error("Validation error on dimensional taxonomy: the target of an 'all' arc is not a hypercube declaration. Id of target: " + to.HrefResourceId);
                        dts.ValidatedDimension = false;
                    }
                }
            }

            // Specification 2.3.1.1
            // A has-hypercube arc MUST have an @xbrldt:contextElement attribute. A dimensional processor MUST raise an error xbrldte:HasHypercubeMissingContextElementAttributeError if this rule is violated.                                
            if (arc.ContextElement == null)
            {
                log.Error("Validation error on dimensional taxonomy:  A has-hypercube arc MUST have an @xbrldt:contextElement attribute. Id of source: " + from.HrefResourceId + " Id of source: " + to.HrefResourceId);
                dts.ValidatedDimension = false;
            }
        }


        private void ValidateConstraintsOnNotAllArcs(Arc arc, Locator from, Concept conceptFrom, Locator to, Concept conceptTo)
        {
            //Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Validating Dimensional Taxonomy: ConstraintsOnNotAllArcs"));

            if (!(conceptFrom.SubstitutionGroup == Concept.EnumSubstitutionGroup.Item))
            {
                log.Error("Validation error on dimensional taxonomy:  if the source of an 'not all' arc is not a primary item declaration. Id of source: " + from.HrefResourceId);
                dts.ValidatedDimension = false;
            }

            if (!(conceptTo.SubstitutionGroup == Concept.EnumSubstitutionGroup.HypercubeItem))
            {
                log.Error("Validation error on dimensional taxonomy: the target of an 'not all' arc is not a hypercube declaration. Id of target: " + to.HrefResourceId);
                dts.ValidatedDimension = false;
            }

            // Specification 2.3.1.1
            // A has-hypercube arc MUST have an @xbrldt:contextElement attribute. A dimensional processor MUST raise an error xbrldte:HasHypercubeMissingContextElementAttributeError if this rule is violated.                                
            if (arc.ContextElement == null)
            {
                log.Error("Validation error on dimensional taxonomy:  A has-hypercube arc MUST have an @xbrldt:contextElement attribute. Id of source: " + from.HrefResourceId + " Id of source: " + to.HrefResourceId);
                dts.ValidatedDimension = false;
            }
        }


        private void ValidateConstraintsOnHypercubeDimensionArcs(Arc arc, Locator from, Concept conceptFrom, Locator to, Concept conceptTo)
        {
            //Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Validating Dimensional Taxonomy: ConstraintsOnHypercubeDimensionArcs"));
            
            if (!(conceptFrom.SubstitutionGroup == Concept.EnumSubstitutionGroup.Unknown) &&
                !(conceptFrom.SubstitutionGroup == Concept.EnumSubstitutionGroup.HypercubeItem))
            {
                log.Error("Validation error on dimensional taxonomy: The source of a hypercube-dimension arc MUST be a hypercube declaration. Id of source: " + from.HrefResourceId);
                dts.ValidatedDimension = false;
            }
            else
            {
                if (conceptFrom.SubstitutionGroup == Concept.EnumSubstitutionGroup.Unknown)
                {
                    Concept otherConcept = dts.GetConceptByName(conceptFrom.SchemaElement.SubstitutionGroup.Namespace, conceptFrom.SchemaElement.SubstitutionGroup.Name);

                    if (!(otherConcept.SubstitutionGroup == Concept.EnumSubstitutionGroup.HypercubeItem))
                    {
                        log.Error("Validation error on dimensional taxonomy: The source of a hypercube-dimension arc MUST be a hypercube declaration. Id of source: " + from.HrefResourceId);
                        dts.ValidatedDimension = false;
                    }
                }
            }

            if (!(conceptFrom.SubstitutionGroup == Concept.EnumSubstitutionGroup.Unknown) &&
                !(conceptTo.SubstitutionGroup == Concept.EnumSubstitutionGroup.DimensionItem))
            {
                log.Error("Validation error on dimensional taxonomy: The target of a hypercube-dimension arc MUST be a dimension declaration. Id of target: " + to.HrefResourceId);
                dts.ValidatedDimension = false;
            }
            else
            {
                if (conceptTo.SubstitutionGroup == Concept.EnumSubstitutionGroup.Unknown)
                {
                    Concept otherConcept = dts.GetConceptByName(conceptTo.SchemaElement.SubstitutionGroup.Namespace, conceptTo.SchemaElement.SubstitutionGroup.Name);

                    if (!(otherConcept.SubstitutionGroup == Concept.EnumSubstitutionGroup.DimensionItem))
                    {
                        log.Error("Validation error on dimensional taxonomy: The target of a hypercube-dimension arc MUST be a dimension declaration. Id of target: " + to.HrefResourceId);
                        dts.ValidatedDimension = false;
                    }
                }
            }
        }

        // XDT 1.0 Specification 2.5.3.1.1 Constraints on the dimension-domain arcs
        private void ValidateConstraintsOnDimensionDomainArcs(Arc arc, Locator from, Concept conceptFrom, Locator to, Concept conceptTo)
        {
            //Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Validating Dimensional Taxonomy: ConstraintsOnDimensionDomainArcs"));
            
            XmlAttribute typedDomainRefAttr = null;

            typedDomainRefAttr = GetTypedDomainRef(conceptFrom);

            if (!(conceptFrom.SubstitutionGroup == Concept.EnumSubstitutionGroup.Unknown) &&
                !(conceptFrom.SubstitutionGroup == Concept.EnumSubstitutionGroup.DimensionItem) || typedDomainRefAttr != null)
            {
                log.Error("Validation error on dimensional taxonomy: The source of a dimension-domain arc MUST be a  explicit dimension declaration. Id of source: " + from.HrefResourceId);
                dts.ValidatedDimension = false;
            }
            else
            {
                if (conceptFrom.SubstitutionGroup == Concept.EnumSubstitutionGroup.Unknown)
                {
                    Concept otherConcept = dts.GetConceptByName(conceptFrom.SchemaElement.SubstitutionGroup.Namespace, conceptFrom.SchemaElement.SubstitutionGroup.Name);

                    if (!(otherConcept.SubstitutionGroup == Concept.EnumSubstitutionGroup.DimensionItem) || typedDomainRefAttr != null)
                    {
                        log.Error("Validation error on dimensional taxonomy: The source of a dimension-domain arc MUST be a  explicit dimension declaration. Id of source: " + from.HrefResourceId);
                        dts.ValidatedDimension = false;
                    }
                }
            }

            if (!(conceptFrom.SubstitutionGroup == Concept.EnumSubstitutionGroup.Unknown) &&
                !(conceptTo.SubstitutionGroup == Concept.EnumSubstitutionGroup.Item))
            {
                log.Error("Validation error on dimensional taxonomy: The target of a dimension-domain arc MUST be a domain member declaration. Id of target: " + to.HrefResourceId);
                dts.ValidatedDimension = false;
            }
            else
            {
                if (conceptTo.SubstitutionGroup == Concept.EnumSubstitutionGroup.Unknown)
                {
                    Concept otherConcept = dts.GetConceptByName(conceptTo.SchemaElement.SubstitutionGroup.Namespace, conceptTo.SchemaElement.SubstitutionGroup.Name);

                    if (!(otherConcept.SubstitutionGroup == Concept.EnumSubstitutionGroup.Item))
                    {
                        log.Error("Validation error on dimensional taxonomy: The target of a dimension-domain arc MUST be a domain member declaration. Id of target: " + to.HrefResourceId);
                        dts.ValidatedDimension = false;
                    }
                }
            }            
        }


        private void ValidateConstraintsOnDomainMemberArcs(Arc arc, Locator from, Concept conceptFrom, Locator to, Concept conceptTo)
        {
            //Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Validating Dimensional Taxonomy: ConstraintsOnDomainMemberArcs"));
            
            if (!(conceptFrom.SubstitutionGroup == Concept.EnumSubstitutionGroup.Unknown) &&
                !(conceptFrom.SubstitutionGroup == Concept.EnumSubstitutionGroup.Item))
            {
                log.Error("Validation error on dimensional taxonomy:  if the source of an 'domain-member' arc is not a primary item declaration. Id of source: " + from.HrefResourceId);
                dts.ValidatedDimension = false;
            }
            else
            {
                if (conceptFrom.SubstitutionGroup == Concept.EnumSubstitutionGroup.Unknown)
                {
                    Concept otherConcept = dts.GetConceptByName(conceptFrom.SchemaElement.SubstitutionGroup.Namespace, conceptFrom.SchemaElement.SubstitutionGroup.Name);

                    if (!(otherConcept.SubstitutionGroup == Concept.EnumSubstitutionGroup.Item))
                    {
                        log.Error("Validation error on dimensional taxonomy:  if the source of an 'domain-member' arc is not a primary item declaration. Id of source: " + from.HrefResourceId);
                        dts.ValidatedDimension = false;
                    }
                }
            }

            if (!(conceptTo.SubstitutionGroup == Concept.EnumSubstitutionGroup.Unknown) && 
                !(conceptTo.SubstitutionGroup == Concept.EnumSubstitutionGroup.Item))
            {
                log.Error("Validation error on dimensional taxonomy: the target of a domain-member arc is not a primary item declaration. Id of target: " + to.HrefResourceId);
                dts.ValidatedDimension = false;
            }
            else
            {
                if (conceptTo.SubstitutionGroup == Concept.EnumSubstitutionGroup.Unknown)
                {
                    Concept otherConcept = dts.GetConceptByName(conceptTo.SchemaElement.SubstitutionGroup.Namespace, conceptTo.SchemaElement.SubstitutionGroup.Name);

                    if (!(otherConcept.SubstitutionGroup == Concept.EnumSubstitutionGroup.Item))
                    {
                        log.Error("Validation error on dimensional taxonomy: the target of a domain-member arc is not a primary item declaration. Id of target: " + to.HrefResourceId);
                        dts.ValidatedDimension = false;
                    }
                }
            }
        }


        private void ValidateConstraintsOnDimensionDefaultArcs(Arc arc, Locator from, Concept conceptFrom, Locator to, Concept conceptTo)
        {
            //Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Validating Dimensional Taxonomy: ConstraintsOnDimensionDefaultArcs"));
            
            if (!(conceptFrom.SubstitutionGroup == Concept.EnumSubstitutionGroup.Unknown) && 
                !(conceptFrom.SubstitutionGroup == Concept.EnumSubstitutionGroup.DimensionItem))
            {
                log.Error("Validation error on dimensional taxonomy: the source of the arc is not an explicit dimension declaration. Id of source: " + from.HrefResourceId);
                dts.ValidatedDimension = false;
            }
            else
            {
                if (conceptFrom.SubstitutionGroup == Concept.EnumSubstitutionGroup.Unknown)
                {
                    Concept otherConcept = dts.GetConceptByName(conceptFrom.SchemaElement.SubstitutionGroup.Namespace, conceptFrom.SchemaElement.SubstitutionGroup.Name);

                    if (!(otherConcept.SubstitutionGroup == Concept.EnumSubstitutionGroup.Item))
                    {
                        log.Error("Validation error on dimensional taxonomy: the source of the arc is not an explicit dimension declaration. Id of source: " + from.HrefResourceId);
                        dts.ValidatedDimension = false;
                    }
                }
            }

            if (!(conceptTo.SubstitutionGroup == Concept.EnumSubstitutionGroup.Unknown) && 
                !(conceptTo.SubstitutionGroup == Concept.EnumSubstitutionGroup.Item))
            {
                log.Error("Validation error on dimensional taxonomy: the target of the arc is not a domain member declaration . Id of target: " + to.HrefResourceId);
                dts.ValidatedDimension = false;
            }
            else
            {
                if (conceptTo.SubstitutionGroup == Concept.EnumSubstitutionGroup.Unknown)
                {
                    Concept otherConcept = dts.GetConceptByName(conceptTo.SchemaElement.SubstitutionGroup.Namespace, conceptTo.SchemaElement.SubstitutionGroup.Name);

                    if (!(otherConcept.SubstitutionGroup == Concept.EnumSubstitutionGroup.Item))
                    {
                        log.Error("Validation error on dimensional taxonomy: the target of the arc is not a domain member declaration . Id of target: " + to.HrefResourceId);
                        dts.ValidatedDimension = false;
                    }
                }
            }           
        }


        // if there is a cycle in which the Primary Item source of the hypercube is also a member of the domain of valid members.

        private void ValidateCycleInPrimaryItem()
        {
            Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Validating Dimensional Taxonomy: ValidateCycleInPrimaryItem"));

            foreach (DimensionalRelationshipSet drs in dts.DtsDimension.DimensionalRelationshipSets)
            {
                foreach (ConsecutiveRelationshipsSet crs in drs.ConsecutiveRelationshipsSets)
                {
                    // polymorphism direct Error

                    foreach (Concept member in crs.Members)
                    {
                        if (crs.HasHypercube.PrimaryItem.Name.Equals(member.Name))
                        {
                            log.Error("Validation error on dimensional taxonomy: There is a cycle in which the Primary Item source of the hypercube is also a member of the domain of valid members.");
                            dts.ValidatedDimension = false;
                        }
                    }

                    // polymorphism Indirect Error

                    foreach (Inheritance inheritance in dts.DtsDimension.Inheritances)
                    {
                        if (inheritance.PrimaryItem == crs.HasHypercube.PrimaryItem)
                        {
                            foreach (Concept conceptInherited in inheritance.PrimaryItemsInherited)
                            {
                                foreach (Concept member in crs.Members)
                                {
                                    if (conceptInherited.Name.Equals(member.Name))
                                    {
                                        log.Error("Validation error on dimensional taxonomy: There is a cycle in which the Primary Item source of the hypercube is also a member of the domain of valid members.");
                                        dts.ValidatedDimension = false;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }


        /// <summary>
        /// XDT 1.0 Specification 2.7.1
        /// If one dimension-default arc exists in one extended link, then the default member is considered as
        /// defined in all the extended links where the dimension is used and the domain contains the domain 
        /// member indicated in the target of the arc.
        /// </summary>
        private void VerifyOccurrencesOnDimensionDefaultArcs()
        {
            Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Validating Dimensional Taxonomy: VerifyOccurrencesOnDimensionDefaultArcs"));

            foreach (NetworkGraph ng in dts.NetworkGraphs)
            {
                int count = 1;

                if (ng.TupleNG.Item4.Equals("http://xbrl.org/int/dim/arcrole/dimension-default"))
                {
                    foreach (Arc defaultArc in ng.NetworkRelationships)
                    {
                        foreach (NetworkGraph ngToCompare in dts.NetworkGraphs)
                        {
                            if (ngToCompare.TupleNG.Item4.Equals("http://xbrl.org/int/dim/arcrole/dimension-default"))
                            {
                                foreach (Arc deafaultArcToCompare in ngToCompare.NetworkRelationships)
                                {
                                    if (defaultArc != deafaultArcToCompare)
                                    {
                                        Locator dimension = GetLocator(defaultArc.ExtendedLinkParent.Locators, defaultArc.FromId);
                                        Locator domain = GetLocator(defaultArc.ExtendedLinkParent.Locators, defaultArc.ToId);
                                        Locator dimensionToCompare = GetLocator(deafaultArcToCompare.ExtendedLinkParent.Locators, deafaultArcToCompare.FromId);
                                        Locator domainToCompare = GetLocator(deafaultArcToCompare.ExtendedLinkParent.Locators, deafaultArcToCompare.ToId);

                                        if (dimension.HrefResourceId.Equals(dimensionToCompare.HrefResourceId) &&
                                            !domain.HrefResourceId.Equals(domainToCompare.HrefResourceId))
                                        {
                                            count++;
                                        }
                                    }
                                }
                            }
                        }

                        if (count > 1)
                        {
                            log.Error("Validation error on dimensional taxonomy: A dimension MUST NOT have more than one default member. Dimension: " + defaultArc.FromId);
                            dts.ValidatedDimension = false;
                            break;
                        }
                    }

                    // The default member indicated in the target of a dimension-default arc is global. If one dimension-default arc exists 
                    // in one extended link, then the default member is considered as defined in all the extended links where the dimension 
                    // is used and the domain contains the domain member indicated in the target of the arc. 

                    foreach (Arc defaultArc in ng.NetworkRelationships)
                    {
                        foreach (Dimension dimension in dts.DtsDimension.Dimensions)
                        {
                            if (dimension.Domains.Contains(dts.GetConceptByID(defaultArc.ToId)))
                            {
                                log.Error("Validation error on dimensional taxonomy: The default member indicated in a dimension-default arc is used in other arc dimension-domain. Default member: " + defaultArc.ToId);
                                dts.ValidatedDimension = false;
                            }
                        }
                    }
                }
            }
        }


        private static XmlAttribute GetTypedDomainRef(Concept conceptFrom)
        {
            XmlAttribute typedDomainRefAttr = null;

            if (conceptFrom.SchemaElement.UnhandledAttributes != null)
            {
                foreach (XmlAttribute attr in conceptFrom.SchemaElement.UnhandledAttributes)
                {
                    if (attr.Name.Equals("xbrldt:typedDomainRef"))
                    {
                        typedDomainRefAttr = attr;
                    }
                }
            }
            return typedDomainRefAttr;
        }


        private bool isCustomArcRole(string arcRole)
        {
            foreach (ArcRoleType arcRoleType in dts.ArcRoleTypes)
            {
                if (arcRole.Equals(arcRoleType.ArcRoleUri.ToString()))
                {
                    return true;
                }
            }

            return false;
        }


        private bool isDefaultRole(string role)
        {
            if (role.Equals("http://www.xbrl.org/2003/role/link"))
            {
                return true;
            }

            return false;
        }


        private void ValidateHypercubeIsAbstract()
        {
            foreach (Hypercube hipercube in dts.DtsDimension.Hypercubes)
            {
                if (!hipercube.Concept.SchemaElement.IsAbstract)
                {
                    log.Error("Validation error on dimensional taxonomy: The hypercube declaration is not abstract. Hypercube: " + hipercube.Concept.Name);
                    dts.ValidatedDimension = false;
                }
            }
        }


        private void ValidateDimensionIsAbstract()
        {
            foreach (Dimension dimension in dts.DtsDimension.Dimensions)
            {
                if (!dimension.Concept.SchemaElement.IsAbstract)
                {
                    log.Error("Validation error on dimensional taxonomy: The dimension declaration is not abstract. Hypercube: " + dimension.Concept.Name);
                    dts.ValidatedDimension = false;
                }
            }
        }


        // XDT 1.0 Specification 2.5.2.1.1 Constraints on the @xbrldt:typedDomainRef attribute

        private void ValidateConstraintsOnTypedDomainRefAttribute()
        {
            // A dimensional processor MUST raise an error xbrldte:TypedDomainRefError if the @xbrldt:typedDomainRef attribute 
            // appears on an XML Schema element declaration that is not a dimension declaration

            Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Validating Dimensional Taxonomy: ConstraintsOnTypedDomainRefAttribute"));

            foreach (Concept concept in dts.GetAllConcepts())
            {
                if (concept.SchemaElement.UnhandledAttributes != null)
                {
                    foreach (XmlAttribute attr in concept.SchemaElement.UnhandledAttributes)
                    {
                        if (attr.Name.Equals("xbrldt:typedDomainRef") && !(concept.SubstitutionGroup == Concept.EnumSubstitutionGroup.Unknown) && 
                                                                         !(concept.SubstitutionGroup == Concept.EnumSubstitutionGroup.DimensionItem))
                        {
                            log.Error("Validation error on dimensional taxonomy: The @xbrldt:typedDomainRef attribute appears on an XML Schema element declaration that is not a dimension declaration. concept: " + concept.Name);
                            dts.ValidatedDimension = false;
                        }
                        else
                        {
                            if (concept.SubstitutionGroup == Concept.EnumSubstitutionGroup.Unknown)
                            {
                                Concept otherConcept = dts.GetConceptByName(concept.SchemaElement.SubstitutionGroup.Namespace, concept.SchemaElement.SubstitutionGroup.Name);

                                if (attr.Name.Equals("xbrldt:typedDomainRef") && !(otherConcept.SubstitutionGroup == Concept.EnumSubstitutionGroup.DimensionItem))
                                {
                                    log.Error("Validation error on dimensional taxonomy: The @xbrldt:typedDomainRef attribute appears on an XML Schema element declaration that is not a dimension declaration. concept: " + concept.Name);
                                    dts.ValidatedDimension = false;
                                }
                            }
                        }
                    }
                }
            }

            // A dimensional processor MUST raise an error xbrldte:OutOfDTSSchemaError attribute points to a schema that is not included in the taxonomy DTS.

            foreach (Dimension dimension in dts.DtsDimension.Dimensions)
            {
                foreach (XmlAttribute attr in dimension.Concept.SchemaElement.UnhandledAttributes)
                {
                    if (attr.Name.Equals("xbrldt:typedDomainRef"))
                    {                        
                        string Href = attr.Value;
                        HyperlinkReference xp = new HyperlinkReference(Href);

                        Concept concept = dts.GetConceptByID(xp.ElementId);

                        if (concept == null)
                        {
                            log.Error("Validation error on dimensional taxonomy: The attribute points to a schema that is not included in the taxonomy DTS. concept: " + dimension.Concept.Name);
                            dts.ValidatedDimension = false;
                        }
                        else
                        {
                            if (concept.SchemaElement.IsAbstract)
                            {
                                log.Error("Validation error on dimensional taxonomy: The attribute points to a global abstract element declaration. concept: " + dimension.Concept.Name);
                                dts.ValidatedDimension = false;
                            }
                        }
                    }
                }
            }
        }


        private Locator GetLocator(IndexedList<Locator> locators, string label)
        {
            return locators.ContainsKeyByName(label.GetHashCode()) ? locators.GetItemByName(label.GetHashCode()) : null;

            //foreach (Locator loc in locators)
            //{
            //    if (loc.Label.Equals(label))
            //    {
            //        return loc;
            //    }
            //}

            //return null;
        }

       
        private List<Locator> distinctLocators;


        private void ValidateCycleInDRS()
        {
            foreach (DimensionalRelationshipSet drs in dts.DtsDimension.DimensionalRelationshipSets)
            {
                distinctLocators = dts.GetDistinctLocators(drs.ArcRelationshipSet);

                if (HasDirectedCycle())
                {
                    log.Error("Validation error on dimensional taxonomy: A consecutive relationship set contains a directed cycle.");
                    dts.ValidatedDimension = false;
                }
            }
        }


        /// <summary>
        ///  A method that validate weather the linkbase document has directed cycle.
        /// </summary>
        public bool HasDirectedCycle()
        {
            // All Vertices in the Graph
            if (distinctLocators == null || distinctLocators.Count == 0)
            {
                return false;
            }

            Queue<Locator> q; // Queue will store vertices that have in-degree of zero
            long counter = 0;

            // Calculate the in-degree of all vertices and store on the Locator 
            foreach (Locator l in distinctLocators)
            {
                l.InDegree = 0;
            }

            foreach (Locator loc in distinctLocators)
            {
                foreach (Arc arc in loc.OutArcs)
                {
                    Locator toLocator = GetLocator(arc.ToLocator.Label);

                    if (toLocator != null)
                    {
                        toLocator.InDegree++;
                    }
                }
            }

            // Find all vertices with in-degree == 0 and put in queue 
            q = new Queue<Locator>();

            foreach (Locator loc in distinctLocators)
            {
                if (loc.InDegree == 0)
                    q.Enqueue(loc);
            }

            if (q.Count == 0)
            {
                return true; // Cycle found – all vertices have in-bound edges
            }

            // Traverse the queue counting Vertices visited 
            for (counter = 0; q.Count != 0; counter++)
            {
                Locator loc = q.Dequeue();

                foreach (Arc arc in loc.OutArcs)
                {
                    Locator to = GetLocator(arc.ToLocator.Label);

                    if (to != null)
                    {
                        to.InDegree--;

                        if (to.InDegree == 0)
                        {
                            q.Enqueue(to);
                        }
                    }
                }
            }

            if (counter != distinctLocators.Count)
            {
                return true;  //Cycle found
            }

            return false;
        }       


        private void SetLocatorArcs(Locator loc, List<Arc> networkRelationship)
        {
            foreach (Arc arc in networkRelationship)
            {
                if (loc.Label.Equals(arc.FromId))
                {
                    if (!loc.OutArcs.Contains(arc))
                    {
                        loc.OutArcs.AddLast(arc);
                    }
                }

                if (loc.Label.Equals(arc.ToId))
                {
                    if (!loc.InArcs.Contains(arc))
                    {
                        loc.InArcs.AddLast(arc);
                    }
                }
            }
        }


        private Locator GetLocator(string label)
        {
            //return distinctLocators.ContainsKeyByName(label.GetHashCode()) ? distinctLocators.GetItemByName(label.GetHashCode()) : null;

            foreach (Locator loc in distinctLocators)
            {
                if (loc.Label.Equals(label))
                {
                    return loc;
                }
            }

            return null;
        }

    }

}
